#include "stdafx.h"
#include <MyControls/Xline.h>
#include <MyControls/Resource.h>
#include <MyControls/App.h>

namespace My {

const int NavigatorHeight	= 8;
const int ThumbLineHeight	= 13;
const int ZoomSliderWidth	= 60;
const int ZoomButtonWidth	= 25;

// XlineHeader

XlineHeader::XlineHeader(Xline & xline) :
	m_Xline(xline)
{
	m_Xline.m_Box.Add(this);
	m_Xline.Remove(&m_Xline.GetTimeRuler());
	Add(&m_Xline.GetTimeRuler());
}

My::Size XlineHeader::GetHeaderHeight() const
{
	return m_Xline.GetTimeRuler().GetPreferredSize().cy;
}

CRect XlineHeader::Bounds() const
{
	CRect r(CPoint(0, 0), m_Xline.m_Box.GetContentSize());
	r.bottom = GetHeaderHeight();
	return r;
}

void XlineHeader::LayoutControls()
{
	Span s = m_Xline.m_TimeCol.GetBounds();
	CRect r = ClientRect();
	r.left = s.first;
	r.right = s.last;
	m_Xline.GetTimeRuler().SetBounds(r);
}

void XlineHeader::HandlePaint(CDC * pDC, const CRect & rClip)
{
	CBrush b(GetSysColor(COLOR_BTNFACE));
	pDC->FillRect(&rClip, &b);

	CRect rClient = ClientRect();
	CRect rCell(rClient);
	for (My::Size i = 0; i < m_Xline.m_Box.m_Grid.GetColumns().GetVisibleColumnCount(); ++i) {
		Column & col = m_Xline.m_Box.m_Grid.GetColumns().GetVisibleColumn(i);
		Span s = col.GetBounds();
		rCell.left = s.first;
		rCell.right = s.last;
		CRect rCellClip;
		rCellClip.IntersectRect(&rCell, &rClip);
		if (rCellClip.IsRectEmpty())
			continue;
		PaintCell(pDC, rCellClip, col, rCell);
	}
	rCell.left = rCell.right;
	rCell.right = rClient.right + 1;
	pDC->Draw3dRect(&rCell, GetSysColor(COLOR_BTNHILIGHT), GetSysColor(COLOR_BTNSHADOW));
};

void XlineHeader::PaintCell(CDC * pDC, const CRect & rClip, const Column & col, const CRect & rCell)
{
	pDC->Draw3dRect(&rCell, GetSysColor(COLOR_BTNHILIGHT), GetSysColor(COLOR_BTNSHADOW));
	String text = col.GetCaption();
	CRect r(rCell);
	r.InflateRect(-2, -2);
	pDC->SetBkMode(TRANSPARENT);
	if (!text.empty())
		pDC->DrawText(text.c_str(), &r, DT_SINGLELINE|DT_END_ELLIPSIS|DT_LEFT|DT_VCENTER);
}

// XlineGrid::RubberRect

XlineGrid::RubberRect::RubberRect(XlineGrid & grid) :
	m_Grid(grid)
{
	m_Visible = false;
	m_Shown = false;
}
		
void XlineGrid::RubberRect::PinDown(CPoint p)
{
	m_Box = CRect(p.x, p.y, p.x, p.y);
	if (m_Visible) {
		Hide();
		Show();
	}
}

void XlineGrid::RubberRect::Stretch(CPoint p)
{
	m_Box.right = p.x;
	m_Box.top = p.y;

	if (m_Visible) {
		Hide();
		Show();
	}
}

void XlineGrid::RubberRect::SetVisible(bool on)
{
	m_Visible = on;
	if (m_Visible)
		Show();
	else
		Hide();
}

CRect XlineGrid::RubberRect::GetBox()
{
	CRect rect = m_Box;
	rect.NormalizeRect();
	return rect;
}

void XlineGrid::RubberRect::Show()
{
	if (m_Shown)
		return;
	m_Grid.Update();
	ClientDC dc(&m_Grid);
	Draw(&dc);
	m_Shown = true;
}

void XlineGrid::RubberRect::Hide()
{
	if (!m_Shown)
		return;
	CRect r(m_Rect);

	ClientDC dc(&m_Grid);
	CRect rClip(m_Grid.ClientRect());
	Span s = m_Grid.m_Xline.GetTimeCol().GetBounds();
	rClip.left = s.first;
	rClip.right = s.last;

	ClipRect clip(&m_Grid, &dc, rClip);
	dc.DrawFocusRect(&r);
	m_Shown = false;
}

void XlineGrid::RubberRect::Draw(CDC * pDC)
{
	CRect rClip(m_Grid.ClientRect());
	Span s = m_Grid.m_Xline.GetTimeCol().GetBounds();
	rClip.left = s.first;
	rClip.right = s.last;

	ClipRect clip(&m_Grid, pDC, rClip);
	m_Rect = GetBox();
	pDC->DrawFocusRect(&m_Rect);
}

// XlineGrid

XlineGrid::XlineGrid(Xline & xline) :
	List(xline.m_Box), 
	Grid(xline.m_Box), 
	Tree(xline.m_Box), 
	m_Xline(xline),
	m_RubberRect(* this),
	m_DotLineVisible(false),
	m_InsertionVisible(false)
{
	m_SmallFont.CreateFont(-10, 0, 0, 0, 400, FALSE, FALSE, 0,  DEFAULT_CHARSET, 0, 0, NONANTIALIASED_QUALITY, 0, "Tahoma");
}

void XlineGrid::ShowInsertion(bool on, My::Size iRow)
{
	if (m_InsertionVisible != on || m_InsertionRow != iRow) {
		TRACE2("ShowInsertion(%s, %d) {\n", on ? "true" : "false", iRow);
		if (m_InsertionVisible)
			// Hide previous insertion
			DrawInsertion();
		m_InsertionVisible = on && (iRow <= GetVisibleRowCount());
		m_InsertionRow = iRow;
		if (m_InsertionVisible) {
			// Show new insertion
			if (m_InsertionRow < GetVisibleRowCount())
				m_InsertionY = GetVisibleRow(m_InsertionRow).Bounds().top;
			else
				m_InsertionY = GetVisibleRow(m_InsertionRow - 1).Bounds().bottom;
			DrawInsertion();
		}
		TRACE0("} // ShowInsertion\n");
	}
}

CRect XlineGrid::Bounds() const
{
	CRect r(CPoint(0, 0), m_Xline.m_Box.GetContentSize());
	r.top = m_Xline.m_Box.m_Header.GetHeaderHeight();
	return r;
}

void XlineGrid::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	Grid::HandleLeftDown(flags, p);
}

void XlineGrid::HandleLeftUp(unsigned int flags, const CPoint & p)
{
	if (!Dragging())
		if ((GetAsyncKeyState(VK_CONTROL) & (~0 << 1)) == 0)
			GetApp().GetDoc().SetSelection(0);
	Grid::HandleLeftUp(flags, p);
}

bool XlineGrid::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	m_RubberRect.PinDown(p0);
	m_RubberRect.SetVisible(true);
	return true;
}

void XlineGrid::HandleDragging(unsigned int, const CPoint & p)
{
	m_RubberRect.Stretch(p);
}

void XlineGrid::HandleDragEnd(unsigned int, const CPoint &)
{
	m_RubberRect.SetVisible(false);
	CRect rRange = m_RubberRect.GetBox();
	rRange.OffsetRect(- m_Xline.GetTimeCol().GetBounds().first, 0);
	rect<int> range;
	range.x1 = (int) ceil(m_Xline.XToTime(rRange.left));
	range.x2 = (int) floor(m_Xline.XToTime(rRange.right));
	range.y1 = rRange.top;
	range.y2 = rRange.bottom;
	m_Xline.RangeSelect(range);
}

void XlineGrid::HandlePaint(CDC * pDC, const CRect & r)
{
	COLORREF cF = ::GetSysColor(COLOR_BTNFACE);
	COLORREF cS = ::GetSysColor(COLOR_BTNSHADOW);
	COLORREF cL = ::GetSysColor(COLOR_BTNHILIGHT);
	COLORREF cLF = My::MeanColor(cL, cF);
	COLORREF cFS = My::MeanColor(cF, cS);

	CBrush bGround(cLF);
	pDC->FillRect(&r, &bGround);

	CRect rClient(ClientRect());

	CPen pShadow(PS_SOLID, 1, cS);
	pDC->SelectObject(&pShadow);
	for (My::Size i = 0; i < GetColumns().GetVisibleColumnCount() - 1; ++i) {
		Column & col = GetColumns().GetVisibleColumn(i);
		int x = col.GetBounds().last - 1;
		pDC->MoveTo(x, rClient.top);
		pDC->LineTo(x, rClient.bottom);
	}

	CRect rTime(ClientRect());
	Span s = m_Xline.m_TimeCol.GetBounds();
	rTime.left = s.first;
	rTime.right = s.last;

	PaintTimeColumn(pDC, rTime);
}

void XlineGrid::PaintTimeColumn(CDC * pDC, const CRect & rTime)
{
	ClipRect clip(this, pDC, rTime);

	COLORREF cF = ::GetSysColor(COLOR_BTNFACE);
	COLORREF cS = ::GetSysColor(COLOR_BTNSHADOW);
	COLORREF cL = ::GetSysColor(COLOR_BTNHILIGHT);
	COLORREF cLF = My::MeanColor(cL, cF);
	COLORREF cFS = My::MeanColor(cF, cS);
	COLORREF cFFS = My::MeanColor(cF, cFS);
	COLORREF cLFF = My::MeanColor(cLF, cF);
	COLORREF cViolet = RGB(242, 242, 255);
	COLORREF cDViolet = RGB(227, 227, 255);
	COLORREF cDDViolet = RGB(212, 212, 255);

	// Filling the ground

	CBrush bFace(cF);
	pDC->FillRect(&rTime, &bFace);

	if (!m_Xline.IsEmpty()) {

		CRect rWorkArea(rTime);
		rWorkArea.left = rTime.left + m_Xline.TimeToX(m_Xline.GetWorkArea().first);
		rWorkArea.right = rTime.left + m_Xline.TimeToX(m_Xline.GetWorkArea().last);
		rWorkArea.IntersectRect(&rWorkArea, &rTime);

		CBrush bWorkArea(cLF);
		pDC->FillRect(&rWorkArea, &bWorkArea);

		CRect rCurrent(rTime);
		rCurrent.left = rTime.left + m_Xline.TimeToX(m_Xline.GetSequence()->GetCurrentFrame());
		rCurrent.right = rTime.left + m_Xline.TimeToX(m_Xline.GetSequence()->GetCurrentFrame() + 1);

		CBrush bCurrent(cViolet);
		pDC->FillRect(&rCurrent, &bCurrent);

		if (m_RubberRect.IsVisible())
			m_RubberRect.Draw(pDC);
	}
}

void XlineGrid::UpdateBounds()
{
	Grid::UpdateBounds();
	int i = m_Xline.m_TimeCol.GetVisibleIndex();
	int left = i > 0 ? GetColumns().GetVisibleColumn(My::Size(i - 1)).GetBounds().last : 0;
	m_Xline.m_TimeCol.SetWidth(Width() - left);
}

void XlineGrid::Paint(CDC * pDC, const CRect & rClip)
{
	Grid::Paint(pDC, rClip);

	if (!m_Xline.IsEmpty()) {

		Span s = m_Xline.m_TimeCol.GetBounds();
		int x = s.first + m_Xline.TimeToX(m_Xline.GetSequence()->GetCurrentFrame());
		if (s.Inside(x) && rClip.left <= x && rClip.right > x) {
			SaveObjects save(pDC);
			CPen pRed(PS_SOLID, 1, RGB(255, 0, 0));
			pDC->SelectObject(&pRed);
			pDC->MoveTo(x, 0);
			pDC->LineTo(x, Height());
		}
		if (m_Xline.GetSequence()->GetCurrentFrame() != m_Xline.GetSequence()->GetPreviewFrame())
		{
			m_DotLineX = s.first + m_Xline.TimeToX(m_Xline.GetSequence()->GetPreviewFrame());
			if (s.Inside(m_DotLineX)) {
				My::XorAlternateVertLine(pDC, 
					m_DotLineX, 0, Height(), RGB(255, 255, 255));
				m_DotLineVisible = true;
			} else
				m_DotLineVisible = false;
		} else
			m_DotLineVisible = false;

		if (m_InsertionVisible) {
			// Show new insertion
			int y;
			if (m_InsertionRow < GetVisibleRowCount())
				y = GetVisibleRow(m_InsertionRow).Bounds().top;
			else
				y = GetVisibleRow(m_InsertionRow - 1).Bounds().bottom;
//			if (y != m_InsertionY) {
				m_InsertionY = y;
				TRACE0("Paint() { \n");
				DrawInsertion(pDC);
				TRACE0("} // Paint \n");
//			}
		}
	}
}

void XlineGrid::UpdateDotLine()
{
	ClientDC dc(this);
	if (m_DotLineVisible) {
		My::XorAlternateVertLine(&dc, m_DotLineX, 0, Height(), RGB(255, 255, 255));
		m_DotLineVisible = false;
	}
	if (!m_Xline.IsEmpty() && 
		m_Xline.GetSequence()->GetCurrentFrame() != m_Xline.GetSequence()->GetPreviewFrame()) {
		Span s = m_Xline.m_TimeCol.GetBounds();
		m_DotLineX = s.first + m_Xline.TimeToX(m_Xline.GetSequence()->GetPreviewFrame());
		if (s.Inside(m_DotLineX)) {
			My::XorAlternateVertLine(&dc, 
				m_DotLineX, 0, Height(), RGB(255, 255, 255));
			m_DotLineVisible = true;
		} else
			m_DotLineVisible = false;
	}
}

void XlineGrid::DrawInsertion(CDC * pDC)
{
	ClientDC dc(this);
	if (pDC == 0)
		pDC = &dc;
	SaveObjects save(pDC);
	
	CRect rTime = ClientRect();
	//Span s = m_Xline.m_TimeCol.GetBounds();
	//rTime.left = s.first;
	//rTime.right = s.last;
//	ClipRect clip(this, &dc, rTime);

	CPen pen(PS_SOLID, 4, RGB(64, 64, 64));
	pDC->SelectObject(&pen);

	int nROP = pDC->SetROP2(R2_NOTXORPEN);

	TRACE1("DrawInsertion() ... InsertionY = %d\n", m_InsertionY);
	pDC->MoveTo(rTime.left - 4, m_InsertionY);
	pDC->LineTo(rTime.right + 4, m_InsertionY);

	pDC->SetROP2(nROP);
}

void XlineGrid::UpdateContentSize()
{
	CSize s = CSize(GetContentWidth(), GetContentHeight());
	s.cy += m_Xline.m_Box.m_Header.GetHeaderHeight();
	m_Xline.m_Box.SetPreferredContentSize(s);
	Invalidate();
}

void XlineGrid::ProcessEvent(Event & e)
{
	if (e.Is<Sequence::PreviewFrameChange>() 
		|| e.Is<Timeline::CurrentFrameChange>()) 
	{
		UpdateDotLine();
	} 
	else if (e.Is<Timeline::Event>()
		|| e.Is<Sequence::Event>())
	{
		Invalidate();
	}
	else 
		Grid::ProcessEvent(e);
}

// XlineBox

XlineBox::XlineBox(Xline & xline) :
	m_Xline(xline), m_Header(xline), m_Grid(xline)
{
	SetAlignment(ALIGN_STRETCH);

	m_Xline.Add(this);

	GetVertBar().SetAlwaysVisible(true);
	GetHorzBar().Enable(false);
}

CRect XlineBox::Bounds() const
{
	CRect r = m_Xline.ClientRect();
	r.top += NavigatorHeight + ThumbLineHeight;
	r.bottom -= ::GetSystemMetrics(SM_CYHSCROLL);
	return r;
}

void XlineBox::UpdateBounds()
{
	ScrollBox::UpdateBounds();
	m_Grid.UpdateBounds();
	m_Header.UpdateBounds();
}

// Xline

Xline::Xline() :
	m_Box(* this)
{
	Attach(&m_Box.m_Grid);

	m_NameCol.SetWidth(150);
	m_NameCol.m_Caption = GetApp().LoadString(IDS_TIMELINE_NAME);
	m_Box.m_Grid.GetColumns().Append(&m_NameCol);
	m_Box.m_Grid.GetColumns().Append(&m_TimeCol);
}

Xline::~Xline()
{
	Detach(&m_Box.m_Grid);
}

CSize Xline::GetPreferredSize() const
{
	return CSize(0, 
		NavigatorHeight + 
		ThumbLineHeight + 
		m_TimeRuler.GetPreferredSize().cy + 
		::GetSystemMetrics(SM_CYHSCROLL));
}

void Xline::LayoutControls()
{
	m_Box.UpdateBounds();

	Span sTime = m_TimeCol.GetBounds();
	int h = ClientHeight();

	int sbs = ::GetSystemMetrics(SM_CYHSCROLL);

	m_Navigator.SetBounds(sTime.first, 0, sTime.last, NavigatorHeight);
	m_ThumbLine.SetBounds(sTime.first, NavigatorHeight, sTime.last, NavigatorHeight + ThumbLineHeight);
	m_XScrollBar.SetBounds(sTime.first + ZoomSliderWidth + 2 * ZoomButtonWidth, h - sbs, sTime.last, h);
	m_ScaleOut.SetBounds(sTime.first, h - sbs, sTime.first + ZoomButtonWidth, h);
	m_TimeSlider.SetBounds(sTime.first + ZoomButtonWidth, h - sbs, sTime.first + ZoomButtonWidth + ZoomSliderWidth, h);
	m_ScaleIn.SetBounds(sTime.first + ZoomButtonWidth + ZoomSliderWidth, h - sbs, sTime.first + 2 * ZoomButtonWidth + ZoomSliderWidth, h);
}

void Xline::UpdateBounds()
{
	Timeline::UpdateBounds();
}

} // My